package ui;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.TextLayout;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;

public class CustomComponent extends Rectangle {

	protected BufferedImage imgNormal, imgDisabled, imgMouseOver, imgMouseDown;
	protected Path2D.Float shape;
	protected Area area;
	private RoundRectangle2D rect;
	private volatile boolean enabled = true, visible = true, show = true;
	private String label = "";
	private Shape text;
	private Shape icon;

	private volatile float alpha;
	private Thread thdShow, thdScroll;
	private int labelX, labelY;

	private final CustomComponent me = this;

	private boolean clickable, alwaysEnabled;

	public CustomComponent (RoundRectangle2D rect, String label, boolean clickable, boolean alwaysEnabled) {
		this.label = label;
		this.clickable = clickable;
		this.alwaysEnabled = alwaysEnabled;
		initGeneral(rect);
		initShape();
		initImages();
	}

	public CustomComponent (RoundRectangle2D rect, Shape icon, boolean clickable, boolean alwaysEnabled) {
		this.icon = icon;
		this.clickable = clickable;
		this.alwaysEnabled = alwaysEnabled;
		initGeneral(rect);
		initShape();
		initImages();
	}

	private synchronized void initImages () {
		initNormal();
		if (!alwaysEnabled) {
			initDisabled();
		}
		if (clickable) {
			initMouseOver();
			initMouseDown();
		}
	}

	private void initGeneral (RoundRectangle2D rect) {
		this.rect = rect;
		setFrame(rect.getX(), rect.getY(), rect.getWidth() + 2, rect.getHeight() + 2);
		labelY = height;
	}

	private synchronized void initShape () {
		shape = new Path2D.Float(rect);
		shape.setWindingRule(Path2D.WIND_EVEN_ODD);

		imgNormal = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

		Graphics2D g = imgNormal.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

		if (label.length() > 0) {
			text = new TextLayout(label, new Font("Tahoma", Font.BOLD, 16), g.getFontRenderContext()).getOutline(g.getTransform());
			if (labelY == height) {
				labelX = (width - text.getBounds().width) / 2;
				labelY = (height - text.getBounds().height) / 2 + 12;
			}
			FramePane.appendTextShape(shape, text, g.getTransform(), labelX, labelY);
		}
		if (icon != null) {
			if (labelY == height) {
				labelX = 1;
				labelY = 0;
			}
			FramePane.appendTextShape(shape, icon, g.getTransform(), labelX, labelY);
		}
		if (clickable) {
			area = new Area(shape);
			area.intersect(new Area(rect));
		}

		g.dispose();
	}

	private synchronized void initNormal () {
		imgNormal = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

		Graphics2D g = imgNormal.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

		g.setColor(new Color(255, 255, 255, (int) (80 * alpha)));
		if (clickable) {
			g.fill(area);
		} else {
			g.fill(shape);
		}
		g.setColor(new Color(255, 255, 255, (int) (50 * alpha)));
		g.draw(rect);

		imgNormal.flush();
		g.dispose();
	}

	private synchronized void initDisabled () {
		imgDisabled = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

		Graphics2D g = imgDisabled.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

		g.setColor(new Color(255, 255, 255, (int) (40 * alpha)));
		if (clickable) {
			g.fill(area);
		} else {
			g.fill(shape);
		}
		g.setColor(new Color(105, 105, 105, (int) (40 * alpha)));
		g.draw(rect);

		imgDisabled.flush();
		g.dispose();
	}

	private synchronized void initMouseOver () {
		imgMouseOver = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

		Graphics2D g = imgMouseOver.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

		g.setColor(new Color(255, 255, 255, (int) (120 * alpha)));
		g.fill(area);
		g.setColor(new Color(255, 255, 255, (int) (80 * alpha)));
		g.draw(rect);

		imgMouseOver.flush();
		g.dispose();
	}

	private synchronized void initMouseDown () {
		imgMouseDown = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

		Graphics2D g = imgMouseDown.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

		g.setColor(new Color(255, 255, 255, (int) (120 * alpha)));
		g.fill(area);
		g.setColor(new Color(255, 255, 255, (int) (80 * alpha)));
		g.draw(rect);

		imgMouseDown.flush();
		g.dispose();
	}

	public boolean isEnabled () {
		return enabled;
	}

	public void setEnabled (boolean enabled) {
		this.enabled = enabled;
	}

	public synchronized void mouseClicked () {
		if (thdScroll == null || thdScroll.getState().equals(Thread.State.TERMINATED)) {
			thdScroll = new Thread() {
				@Override
				public void run () {
					do {
						synchronized (me) {
							labelX += 2;
							if (labelX >= width) {
								labelX = -(text == null ? icon == null ? 0 : icon.getBounds().width : text.getBounds().width);
							}
							initShape();
							initImages();
							try {
								Thread.sleep(1);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					} while (labelX != (width - (text == null ? icon == null ? 0 : icon.getBounds().width : text.getBounds().width)) / 2 - 1 &&
							labelX != (width - (text == null ? icon == null ? 0 : icon.getBounds().width : text.getBounds().width)) / 2);
				}
			};
			thdScroll.start();
		}
	}

	public synchronized void setVisible (boolean vis) {
		show = vis;
		if (show) {
			visible = true;
		}
		if (thdShow == null || thdShow.getState().equals(Thread.State.TERMINATED)) {
			thdShow = new Thread() {
				@Override
				public void run () {
					while (true) {
						try {
							Thread.sleep(8);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						if (show) {
							alpha += 0.03f;
							if (alpha <= 1) {
								initImages();
							} else {
								alpha = 1f;
								initImages();
								break;
							}
						} else {
							alpha -= 0.03f;
							if (alpha >= 0) {
								initImages();
							} else {
								alpha = 0f;
								initImages();
								break;
							}
						}
					}
					if (!show) {
						visible = show;
					}
				}
			};
			thdShow.start();
		}
	}

	public boolean isVisible () {
		return visible;
	}

	public String getLabel () {
		return label;
	}

	public synchronized void setLabel (String label) {
		this.label = label;
		labelY = height;
		initShape();
		initImages();
	}

	public synchronized void setIcon (Shape icon) {
		this.icon = icon;
		labelY = height;
		initShape();
		initImages();
	}

	public boolean isClickable () {
		return clickable;
	}

	public void setClickable (boolean clickable) {
		this.clickable = clickable;
	}

	public boolean isAlwaysEnabled () {
		return alwaysEnabled;
	}

	public void setAlwaysEnabled (boolean alwaysEnabled) {
		this.alwaysEnabled = alwaysEnabled;
	}
}
